package com.neuronbit.businessflow.intellij.util;

import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiTypesUtil;

import java.util.*;
import java.util.stream.Collectors;

import static com.intellij.psi.util.PsiFormatUtilBase.*;
import static java.util.Objects.requireNonNull;
import static org.apache.commons.lang3.StringUtils.startsWithAny;

/**
 * The type Psi utils.
 */
public class PsiUtils {
    /**
     * Is enum value boolean.
     *
     * @param element the element
     * @return the boolean
     */
    public static boolean isEnumValue(PsiElement element) {
        if (element instanceof PsiField) {
            PsiClass psiClass = PsiTypesUtil.getPsiClass(((PsiField) element).getType());
            return psiClass != null && psiClass.isEnum();
        } else if (element instanceof PsiMethod) {
            PsiClass psiClass = PsiTypesUtil.getPsiClass(((PsiMethod) element).getReturnType());
            return psiClass != null && psiClass.isEnum();
        } else {
            // Handle other PsiTypes if needed
        }
        return false;
    }

    /**
     * Is enum value boolean.
     *
     * @param element the element
     * @return the boolean
     */
    public static boolean isEnumValue(PsiType element) {
        PsiClass psiClass = PsiTypesUtil.getPsiClass(element);
        return psiClass != null && psiClass.isEnum();
    }

    public static PsiType getPsiTypeFromPsiClass(PsiClass psiClass) {
        Project project = psiClass.getProject();
        PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();
        return elementFactory.createType(psiClass);
    }

    /**
     * Retrieve enum values list.
     *
     * @param psiType the psi type
     * @return the list
     */
    public static List<Map<String, String>> retrieveEnumValues(PsiType psiType) {
        PsiClass psiClass = PsiTypesUtil.getPsiClass(psiType);
        if (null != psiClass && psiClass.isEnum()) {
            PsiField[] fields = psiClass.getFields();
            return Arrays.stream(fields)
                    .filter(field -> field.hasModifierProperty(PsiModifier.PUBLIC)
                            && field.hasModifierProperty(PsiModifier.STATIC)
                            && field.hasModifierProperty(PsiModifier.FINAL))
                    .map(field -> {
                        Map<String, String> map = new HashMap<>();
                        map.put("enumValue", field.getName());
                        if (field.getDocComment() != null) {
                            map.put("enumDesc", field.getDocComment().getText());
                        } else {
                            map.put("enumDesc", field.getName());
                        }
                        return map;
                    })
                    .collect(Collectors.toList());
        } else {
            return Collections.emptyList();
        }
    }

    /**
     * Format method string.
     *
     * @param psiMethod the psi method
     * @return the string
     */
    public static String formatMethod(PsiMethod psiMethod) {
        final boolean dumb = DumbService.isDumb(psiMethod.getProject());
        return PsiFormatUtil.formatMethod(psiMethod,
                PsiSubstitutor.EMPTY,
                SHOW_NAME | TYPE_AFTER | SHOW_PARAMETERS | (dumb ? 0 : SHOW_TYPE) | SHOW_FQ_CLASS_NAMES,
                SHOW_NAME | SHOW_TYPE | SHOW_FQ_CLASS_NAMES);
    }

    /**
     * Gets full type name.
     *
     * @param type the type
     * @return the full type name
     */
    public static String getFullTypeName(PsiType type) {
        if (type instanceof PsiPrimitiveType) {
            return type.getPresentableText();
        } else {
            PsiClass returnPsiClass = PsiTypesUtil.getPsiClass(type);
            if (returnPsiClass != null) {
                return returnPsiClass.getQualifiedName();
            } else {
                return "unknown";
            }
        }
    }

    /**
     * Is atomic boolean.
     *
     * @param psiClass the psi class
     * @return the boolean
     */
    public static boolean isAtomic(PsiClass psiClass) {
        return psiClass == null
                || psiClass.isEnum()
                || startsWithAny(requireNonNull(psiClass.getQualifiedName()), "java.lang", "java.math");
    }
}
